#include "b2d.h" 



void BoundaryListener::Violation(b2Body* body)
{
	error("violation!!");
	/*

		test->BoundaryViolated(body);
	*/
}

void ContactListener::Add(const b2ContactPoint* point)
{
	/*

	if (test->pointCount == k_maxContactPoints)
	{
		return;
	}

	ContactPoint* cp = test->m_points + test->pointCount;
	cp->shape1 = point->shape1;
	cp->shape2 = point->shape2;
	cp->position = point->position;
	cp->normal = point->normal;
	cp->id = point->id;
	cp->state = e_contactAdded;

	++test->pointCount;
	*/
}

void ContactListener::Persist(const b2ContactPoint* point)
{
	error("persist!!");

	/*
	if (test->pointCount == k_maxContactPoints)
	{
		return;
	}

	ContactPoint* cp = test->m_points + test->pointCount;
	cp->shape1 = point->shape1;
	cp->shape2 = point->shape2;
	cp->position = point->position;
	cp->normal = point->normal;
	cp->id = point->id;
	cp->state = e_contactPersisted;

	++test->pointCount;
	*/
	
}

void ContactListener::Remove(const b2ContactPoint* point)
{

	error("remove!!");

	/*
	if (test->pointCount == k_maxContactPoints)
	{
		return;
	}

	ContactPoint* cp = test->m_points + test->pointCount;
	cp->shape1 = point->shape1;
	cp->shape2 = point->shape2;
	cp->position = point->position;
	cp->normal = point->normal;
	cp->id = point->id;
	cp->state = e_contactRemoved;

	++test->pointCount;
	*/
	
}

b2d::b2d(t_symbol *s, long ac, t_atom *av) : MaxCpp<b2d>()
{		

	b2mouseDown = false;
	
	/* instantiate attributes that need to be accessed from max/msp */
	initMaxMSPAttributes(s,ac,av);

	/* init the world */
	initworld();
	
		/* get the managers and factories ready */
	userDataManager = new b2UserDataManager(); // delete these?
	objectFactory = new b2ObjectFactory(world,userDataManager);	// delete these?

	
	/* set the debug draw's outlet */
	debugDraw.SetOutlet(main_outlet);

	post("making bodies\n");

	//makestage();
		


/*

	
	b2Body*   body;
	b2BodyDef bodyDef;
	b2CircleDef circleDef;
	b2UserData* pd;
	
	
	
	for(int k = 0; k < 100; k++) {
			
		bodyDef.position.Set(P2M(randomrange(0,1024)),P2M(randomrange(0,768)));
		
		// create the body in the world
		body = world->CreateBody(&bodyDef);
		
		// now add collision shapes to that body (just make abunch of random ones)
		circleDef.radius = P2M(randomrange(5.0f,10.0f));
		circleDef.density = randomrange(0.75f, 10.0f);;
		circleDef.friction = randomrange(0.2f, 0.8f);;
		circleDef.restitution = randomrange(0.2f, 0.8f);
		
		// Add the shape(s) to the body.
		body->CreateShape(&circleDef);
		body->SetMassFromShapes(); // should be zero

		char nn [50]; sprintf (nn, "circle_%d", k);
	   // userDataHash[nn] =  new b2UserData(nn,e_bodyType,body,true); // sets circular reference
	
		
	}

	


	
	
	
	// make a simple massless obstacle
	// reuse the bodyDef and circleDef from above
	
	// create a body at a random position on screen.
	// make sure to use the P2M() and M2P() functions to set the appropriate positions in the b2World
		bodyDef.position.Set(P2M(1000/2.0f),P2M(1000/2.0f));
		
		// create the body in the world
		body = world->CreateBody(&bodyDef);
		
		// now add collision shapes to that body (just make abunch of random ones)
		circleDef.radius = P2M(100);
		circleDef.density = 0;
		circleDef.friction = 0.1f;
		circleDef.restitution = 0.1f;
		
		// Add the shape(s) to the body.
		body->CreateShape(&circleDef);
		body->SetMassFromShapes(); // should be zero
				
		//body->SetUserData(new b2UserData("obstacle",0,true));
*/	

		m_biped = new Biped(world, b2Vec2(P2M(1000/2.0f),P2M(1000/2.0f)),userDataManager);
//		post("the user data hash size is %d", userDataHash.size());
		post("number of bodies => %d",world->GetBodyCount());



}

 b2d::~b2d() {
//	freeobject((t_object*)m_proxy);
	delete userDataManager;
	userDataManager = NULL;
	delete objectFactory;
	objectFactory = NULL;

}




void b2d::initworld() {
	// delete it if needed 
	if(world != NULL) {
		delete world; // delete the world
		userDataManager->reset(); // clear the name has
	}
		
	// set up world bounding box
	b2AABB wAABB;
	wAABB.lowerBound.Set(P2M(worldAABB[0]), P2M(worldAABB[1]));
	wAABB.upperBound.Set(P2M(worldAABB[2]), P2M(worldAABB[3]));
	// the wAABB bb does not get physics data
	
	b2Vec2* g = new b2Vec2(gravity[0], gravity[1]);
	world = new b2World(wAABB, *g, true); // assume do sleep is true
	
//	m_textLine = 30;
	m_mouseJoint = NULL;
	pointCount = 0;

	m_destructionListener.b2d_main = this;
//	m_boundaryListener.test = this;
//	m_contactListener.test = this;
	world->SetDestructionListener(&m_destructionListener);
//	world->SetBoundaryListener(&m_boundaryListener);
//	world->SetContactListener(&m_contactListener);
	world->SetDebugDraw(&debugDraw);
	
	
	
}

void b2d::setGravity(t_symbol * s, long ac, t_atom * av) {

	if(ac && av) {
		if(ac == 2) {
			gravitycnt = 2;
			gravity[0] = atom_getfloat(av);
			gravity[1] = atom_getfloat(av+1);
			
			// set the world! (make sure it is initialized
			if(world !=NULL) {
				b2Vec2* g = new b2Vec2(gravity[0], gravity[1]);
				world->SetGravity(*g);
				
				// wake up the bodies
				for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
					b->WakeUp();
				}
				
				
			}
			
			//return MAX_ERR_NONE; 

		} else {
			error("Gravity must be a list of length == 2");
		}
	}
}


void b2d::setWorldAABB(t_symbol * s, long ac, t_atom * av) {
	if(ac && av) {
		if(ac == 4) {
			worldAABBcnt = 4;
			worldAABB[0] = atom_getfloat(av);
			worldAABB[1] = atom_getfloat(av+1);
			worldAABB[2] = atom_getfloat(av+2);
			worldAABB[3] = atom_getfloat(av+3);
			
			/* TODO: NOW INITIT!! */
			}
		}
				
}


void b2d::step() {


{
	float32 timeStep = hz > 0.0f ? 1.0f / hz : float(0.0f);

	uint32 flags = 0;
	flags += debugDrawFlags[0]	* b2DebugDraw::e_shapeBit;
	flags += debugDrawFlags[1]	* b2DebugDraw::e_jointBit;
	flags += debugDrawFlags[2]	* b2DebugDraw::e_coreShapeBit;
	flags += debugDrawFlags[3]	* b2DebugDraw::e_aabbBit;
	flags += debugDrawFlags[4]	* b2DebugDraw::e_obbBit;
	flags += debugDrawFlags[5]	* b2DebugDraw::e_pairBit;
	flags += debugDrawFlags[10]	* b2DebugDraw::e_centerOfMassBit;
	
	debugDraw.SetFlags(flags);

	world->SetWarmStarting(solverParams[0] > 0);
	world->SetPositionCorrection(solverParams[1] > 0);
	world->SetContinuousPhysics(solverParams[2] > 0);

	pointCount = 0;

	world->Step(timeStep, iterations);

	world->Validate();
	
	if (debugDrawFlags[11])
	{
	/*
		DrawString(5, m_textLine, "proxies(max) = %d(%d), pairs(max) = %d(%d)",
			m_world->GetProxyCount(), b2_maxProxies,
			m_world->GetPairCount(), b2_maxPairs);
		m_textLine += 15;

		DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d",
			m_world->GetBodyCount(), m_world->GetContactCount(), m_world->GetJointCount());
		m_textLine += 15;

		DrawString(5, m_textLine, "heap bytes = %d", b2_byteCount);
		m_textLine += 15;
		*/
	}
	
	
	/*
	if (m_mouseJoint)
	{
		b2Body* body = m_mouseJoint->GetBody2();
		b2Vec2 p1 = body->GetWorldPoint(m_mouseJoint->m_localAnchor);
		b2Vec2 p2 = m_mouseJoint->m_target;

		glPointSize(4.0f);
		glColor3f(0.0f, 1.0f, 0.0f);
		glBegin(GL_POINTS);
		glVertex2f(p1.x, p1.y);
		glVertex2f(p2.x, p2.y);
		glEnd();
		glPointSize(1.0f);

		glColor3f(0.8f, 0.8f, 0.8f);
		glBegin(GL_LINES);
		glVertex2f(p1.x, p1.y);
		glVertex2f(p2.x, p2.y);
		glEnd();
	}
	*/
	
	/*
	

	if(false) // (debugDrawFlags[6])	// draw contact points
	{
		const float32 k_impulseScale = 0.1f;
		const float32 k_axisScale = 0.3f;

		for (int32 i = 0; i < pointCount; ++i)
		{
			ContactPoint* point = m_points + i;

			if (point->state == 0)
			{
				// Add
				debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f));
			}
			else if (point->state == 1)
			{
				// Persist
				debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f));
			}
			else
			{
				// Remove
				debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.95f, 0.3f, 0.3f));
			}

			if (debugDrawFlags[7] == 1)
			{
				b2Vec2 p1 = point->position;
				b2Vec2 p2 = p1 + k_axisScale * point->normal;
				debugDraw.DrawSegment(p1, p2, b2Color(0.4f, 0.9f, 0.4f));
			}
			else if (debugDrawFlags[8] == 1)
			{
//				b2Vec2 p1 = point->position;
//				b2Vec2 p2 = p1 + k_forceScale * point->normalForce * point->normal;
//				debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
			}

			if (debugDrawFlags[9] == 1)
			{
//				b2Vec2 tangent = b2Cross(point->normal, 1.0f);
//				b2Vec2 p1 = point->position;
//				b2Vec2 p2 = p1 + k_forceScale * point->tangentForce * tangent;
//				debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
			}
		}
	}
	
*/

}


// PREVIOUS

/*
	if(world) {
		world->Step(1.0f/hz,iterations);
		//post("stepped");
	} else {
		error("world is not initialized ...");
	}
	
	*/

		
}


void b2d::drawShapes(b2Body* body) {
	
	for (b2Shape* shape = body->GetShapeList(); shape; shape = shape->GetNext())
	{
	
		b2UserData* userdata = (b2UserData*)body->GetUserData();

		if(shape->GetType()==e_polygonShape) {
			
			b2PolygonShape* poly = (b2PolygonShape*)shape; // get the poly shape
			int32 vertexCount = poly->GetVertexCount(); // get the number of vertices in the shape (they are squares)
			const b2Vec2* vertices = poly->GetVertices();
			b2Assert(vertexCount <= b2_maxPolygonVertices);  // this just makes sure there aren't too many 
															 // b2_maxPolygonVertices is defined in b2Settings.h
															 
			int nargs = 0;
			int argpos = 0;
			nargs++; // shape
			nargs++; // shape type
			nargs++; // shape name
			nargs+=(vertexCount * 2); // one for each vertex
			
			t_atom polyshape[nargs]; // body, name, x, y, angle, mass /* TODO OPTIMIZE THIS */ /* ONE OUTPUT BUFFER FOR ALL!!*/
																											
			atom_setsym(polyshape+(argpos++),gensym("shape"));
			atom_setsym(polyshape+(argpos++),gensym("poly"));
			sprintf (nameBuffer, "%s", userdata->name);
			atom_setsym(polyshape+(argpos++),gensym(nameBuffer));//userdata->name));

			for (int32 j = 0; j < vertexCount; j++) {
			
				b2Vec2 worldPoint = body->GetWorldPoint(vertices[j]); // get the "world" points of the shapes
																	   // if you don't get the "world" points
																	   // it will just give you the local vertex points
				
				atom_setfloat(polyshape+(argpos++), M2P(worldPoint.x));
				atom_setfloat(polyshape+(argpos++), M2P(worldPoint.y));
			}
		
			outlet_list(main_outlet,0L,nargs,polyshape); 	
			
		}
		
		else if(shape->GetType()==e_circleShape) {
		
		
			
			b2CircleShape* circle = (b2CircleShape*)shape; // get the circle shape
			float dbRadius = circle->GetRadius();
			b2Vec2 dbPosition = body->GetWorldPoint(circle->GetLocalPosition());//->GetPosition();
			float anglez = body->GetAngle();
			
			int nargs = 7;
			t_atom bodyinfo[nargs]; // body, name, x, y, angle, mass 
			
			atom_setsym(bodyinfo+0,gensym("shape"));
			atom_setsym(bodyinfo+1,gensym("circle"));
			sprintf (nameBuffer, "%s", userdata->name);
			atom_setsym(bodyinfo+2,gensym(nameBuffer));
			atom_setfloat(bodyinfo+3,M2P(dbRadius));
			atom_setfloat(bodyinfo+4,M2P(dbPosition.x));
			atom_setfloat(bodyinfo+5,M2P(dbPosition.y));
			atom_setfloat(bodyinfo+6,anglez); // no M2P here
			
			outlet_list(main_outlet,0L,nargs,bodyinfo); 	
		
		//	post("angle=>%f",anglez);
		//	post("");
		}
	}	



}

void b2d::mouse(t_symbol * s, long ac, t_atom * av) {
		if(ac && av) {
			if(ac == 4) {
				int button = atom_getlong(av);
				int state = atom_getlong(av+1);
				int x = atom_getlong(av+2);
				int y = atom_getlong(av+3);

				b2Vec2 p = b2Vec2(P2M((float)x), P2M((float)y));
				
				if(state == 1) {
					if(!b2mouseDown) {
						b2mouseDown = true;
						mousedown(p);
					} else { // if it's already down then move it
						mousemovement(p);
					}
				} 

				if(state == 0) {
					mouseup();
					b2mouseDown = false;
				}
			
		}
	}
}

void b2d::mousemovement(const b2Vec2& p) {
	if (m_mouseJoint) {
		m_mouseJoint->SetTarget(p);
	}
}

void b2d::mouseup() {
	if (m_mouseJoint) {
		world->DestroyJoint(m_mouseJoint);
		m_mouseJoint = NULL;
	}
}

/* TODO: figure out a way to do this outside in the max environemnt, rather than doing it in here */
void b2d::mousedown(const b2Vec2& p) {

if (m_mouseJoint != NULL)
	{
		return;
	}

	// Make a small box.
	b2AABB aabb;
	b2Vec2 d;
	d.Set(0.001f, 0.001f);
	aabb.lowerBound = p - d;
	aabb.upperBound = p + d;

	// Query the world for overlapping shapes.
	const int32 k_maxCount = 10;
	b2Shape* shapes[k_maxCount];
	int32 count = world->Query(aabb, shapes, k_maxCount);
	b2Body* body = NULL;
	for (int32 i = 0; i < count; ++i)
	{
		b2Body* shapeBody = shapes[i]->GetBody();
		if (shapeBody->IsStatic() == false && shapeBody->GetMass() > 0.0f)
		{
			bool inside = shapes[i]->TestPoint(shapeBody->GetXForm(), p);
			if (inside)
			{
				body = shapes[i]->GetBody();
				break;
			}
		}
	}

	if (body)
	{
		b2MouseJointDef md;
		md.body1 = world->GetGroundBody();
		md.body2 = body;
		md.target = p;
#ifdef TARGET_FLOAT32_IS_FIXED
		md.maxForce = (body->GetMass() < 16.0)? 
			(1000.0f * body->GetMass()) : float32(16000.0);
#else
		md.maxForce = 1000.0f * body->GetMass();
#endif
		m_mouseJoint = (b2MouseJoint*)world->CreateJoint(&md);
		body->WakeUp();
	}

}

void b2d::reset() {
	post("clearing all bodies");
	
	b2Body* node = world->GetBodyList();
	
	while (node)
	{
		b2Body* b = node;
		node = node->GetNext();
		
		
		b2UserData* ud = (b2UserData*)b->GetUserData();
		
		if(ud != NULL) {
			userDataManager->remove(ud->name);
		} else {
//			error("No named body
		}
		
		world->DestroyBody(b);
		
		
		
		
	}
}

void b2d::bang() {
	
	b2UserData* userdata;
	
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
		userdata = (b2UserData*)b->GetUserData();
		
		if (userdata != NULL && userdata->draw) {
			if(b->IsSleeping()) {
			//	post("SLEEPING");
			} else {}
			
			if(true) 
				drawShapes(b);
			
			if(true) {
					
				b2Vec2 dbPosition = b->GetWorldCenter(); // center of mass
				float anglez = b->GetAngle();
				
				outCount = 5;				
				atom_setsym(outValue+0,gensym("body"));
				// fugly way to get a string into char* land ...
				sprintf (nameBuffer, "%s", userdata->name);
				atom_setsym(outValue+1,gensym(nameBuffer));
				atom_setfloat(outValue+2,M2P(dbPosition.x));
				atom_setfloat(outValue+3,M2P(dbPosition.y));
				atom_setfloat(outValue+4,anglez); // no M2P here
				
				/* append userdata if available */
				if(outputuserdata) {
					for(int i = 0; i < (outCount+userdata->ac); i++) {
						outValue[outCount+i] = userdata->av[i];
					}
					outCount+= userdata->ac;
				}
				
	

				outlet_list(main_outlet,0L,outCount,outValue); 	
						
			}
			
		} else {
//			post("this one didn't have any info ...");
		}
	}

}

void b2d::group(t_symbol * s, long ac, t_atom *av) {
	// group set GROUP NUMBER paramname param 
	// group get GROUP NUMBER paramname

	post("in group ---");


}

void b2d::grab(t_symbol * s, long ac, t_atom *av) {

	if(ac == 2) {
	
			float x = atom_getfloat(av);
			float y = atom_getfloat(av+1);

			b2Vec2 p = b2Vec2(P2M(x),P2M(y));
			
			// Make a small box.
			b2AABB aabb;
			b2Vec2 d;
			d.Set(0.001f, 0.001f);
			aabb.lowerBound = p - d;
			aabb.upperBound = p + d;

			// Query the world for overlapping shapes.
			const int32 k_maxCount = 10;
			b2Shape* shapes[k_maxCount];
			int32 count = world->Query(aabb, shapes, k_maxCount);
			b2Body* body = NULL;
			for (int32 i = 0; i < count; ++i)
			{
				b2Body* shapeBody = shapes[i]->GetBody();
				if (shapeBody->IsStatic() == false && shapeBody->GetMass() > 0.0f)
				{
					bool inside = shapes[i]->TestPoint(shapeBody->GetXForm(), p);
					if (inside)
					{
						body = shapes[i]->GetBody();
						break;
					}
				}
			}

			if (body)
			{
				b2UserData* ud = (b2UserData*)body->GetUserData();
				if(ud != NULL) 
					
					outCount = 1;
					
					sprintf (nameBuffer, "%s", ud->name);
					atom_setsym(outValue, gensym(nameBuffer));
					
					outlet_anything(main_outlet, gensym("grabbed"),outCount,outValue);
					
				} else {
					//error("body had no name");
				}
			



	
	
	} else {
		error("grab X Y");
	}




}

void b2d::set(t_symbol * s, long ac, t_atom *av) {
		
	if(ac >= 2) {
		
		char* name = atom_getsym(av)->s_name;
		char* param = atom_getsym(av+1)->s_name;
		
		/* advance past name and param */
		ac-=2;
		av+=2;
		
		if(userDataManager->has(name)) {
						
			b2UserData* ud = userDataManager->get(name);
			
					
			/* deal with userdata groups */
			if(EQUALS(param,"userdata") && ac >= 0) {
				post("Setting userdata!!");
				// copy it over
				
				ud->ac = 0;
				
				for(int i = 0; i < ac; i++) {
					ud->ac++;
					ud->av[i] = av[i];
				}
				
				return;
			}

			
			/* deal with userdataitems */
			if(EQUALS(param,"userdataitem")) {

				if(ac == 2) { // index, item
				
					int index = atom_getlong(av);
					
					if(index >= ud->ac) {
						error("You have specified an index that is too big for the current userdata.  To use larger indices, first initialize userdata of desired length with \"set NAME userdata 0 0 0 0 0 ... N\"");
						return;
					
					} else {
					
	//					ud->ac = MAX((index+1),ud->ac); // set the ac to the bigest index
						
						ud->av[index] = av[1];

						return;
					
					}
					
				} else {
					error("need an index and an item.");
					return;

				}
				
			}

				
							
			
			
			
			
			
		
			switch (ud->b2ObjectType) {
				/* bodies */
				case e_b2BodyType:
					if(setParameter(param, (b2Body*)ud->b2Object, ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
				
					break;
				/* shapes */
				case e_b2CircleShapeType:
					if(setParameter(param, (b2CircleShape*)ud->b2Object, ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;
				case e_b2PolygonShapeType:
					if(setParameter(param, (b2PolygonShape*)ud->b2Object,  ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;
				/* joints */	
				case e_b2DistanceJointType:
					if(setParameter(param, (b2DistanceJoint*)ud->b2Object, ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;		
				case e_b2GearJointType:
					if(setParameter(param, (b2GearJoint*)ud->b2Object,  ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;	
				case e_b2MouseJointType:
					if(setParameter(param, (b2MouseJoint*)ud->b2Object,  ac, av)) {
						outlet_list(main_outlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;	
				case e_b2PrismaticJointType:
					if(setParameter(param, (b2PrismaticJoint*)ud->b2Object, ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;	
				case e_b2PulleyJointType:
					if(setParameter(param, (b2PulleyJoint*)ud->b2Object,  ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;
				case e_b2RevoluteJointType:
					if(setParameter(param, (b2RevoluteJoint*)ud->b2Object, ac, av)) {
						return;
					} else {
						error("couldn't set param");
						return;
					}
					break;
				default:
					//error("couldn't assign user data to object");
					break;
			}
		} else {
			error("no object called \"%s\"",name);
		} 
	
	} else {
		error("to set, need a name and a parameter and value");
	}

}

void b2d::get(t_symbol * s, long ac, t_atom *av) {
	
	post("getting ...");

	if(ac >= 2) {
		
		char* name = atom_getsym(av)->s_name;
		char* param = atom_getsym(av+1)->s_name;
				
		if(userDataManager->has(name)) {
						
			outCount = 0;
			
			// set the name
			atom_setsym(outValue, gensym(name));			
			b2UserData* ud = userDataManager->get(name);


			/* PREPEND THE USERDATA FLAG HERE HERE!!!*/

			/* deal with user data first */
			if(EQUALS(param,"userdata")) {

				/* TODO: make the rest of these behave like this ... so you do't have to do the outlet_list crap */
				outlet_anything(maxObexOutlet,gensym(name),ud->ac,ud->av);
				
				return;
			}

			/* deal with user data first */
			if(EQUALS(param,"userdataitem")) {
				
				if(ac == 3) {
					int index = atom_getlong(av+2);
					
					if(index < ud->ac) {
					
						outCount = 1;
						outValue[0] = ud->av[index];
							
						/* TODO: make the rest of these behave like this ... so you do't have to do the outlet_list crap */
						outlet_anything(maxObexOutlet,gensym(name),outCount,outValue);
					
						return;
					
					} else {
					
						error("Your userdata item index is out of range.");
						return;
					
					
					}



					
				
				} else {
					error("Please specify a userdataitem index");
					return;
				}
			}

			



		
			switch (ud->b2ObjectType) {
				/* bodies */
				case e_b2BodyType:
					if(getParam(param, (b2Body*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
				
					break;
				/* shapes */
				case e_b2CircleShapeType:
					if(getParam(param, (b2CircleShape*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;
				case e_b2PolygonShapeType:
					if(getParam(param, (b2PolygonShape*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;
				/* joints */	
				case e_b2DistanceJointType:
					if(getParam(param, (b2DistanceJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;		
				case e_b2GearJointType:
					if(getParam(param, (b2GearJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;	
				case e_b2MouseJointType:
					if(getParam(param, (b2MouseJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;	
				case e_b2PrismaticJointType:
					if(getParam(param, (b2PrismaticJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;	
				case e_b2PulleyJointType:
					if(getParam(param, (b2PulleyJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;
				case e_b2RevoluteJointType:
					if(getParam(param, (b2RevoluteJoint*)ud->b2Object, outCount, outValue+1)) { /* TODO: this pointer crap is iffy */
						outlet_list(maxObexOutlet, gensym(name), ++outCount, outValue); // back up to 
						return;
					} else {
						error("couldn't get param");
					}
					break;
				default:
					//error("couldn't assign user data to object");
					break;
			}
		
		} else {
			error("no object called \"%s\"",name);
		} 
	
	} else {
		error("to get, need a name and a parameter");
	}


}


/* the catch all anything method -- it it isn'ta assigned as a message or attribute, it goes here */
void b2d::anything(t_symbol * s, long ac, t_atom *av) {
		// use proxy to get current inlet number
		long currentInlet = proxy_getinlet( (t_object *)this);
		
		char* message = s->s_name;
		
		switch (currentInlet) {
			case 0:
				
				if(EQUALS(message,"getproxycount")) {
					
					outCount = 1;
					atom_setlong(outValue, world->GetProxyCount());
					outlet_anything(maxObexOutlet, gensym("proxycount"), outCount,outValue);
				
				} else if(EQUALS(message,"getpaircount")) {
					outCount = 1;
					atom_setlong(outValue, world->GetPairCount());
					outlet_anything(maxObexOutlet, gensym("paircount"), outCount,outValue);
				
				} else if(EQUALS(message,"getbodycount")) {
					outCount = 1;
					atom_setlong(outValue, world->GetBodyCount());
					outlet_anything(maxObexOutlet, gensym("bodycount"), outCount,outValue);

				} else if(EQUALS(message,"getjointcount")) {
					outCount = 1;
					atom_setlong(outValue, world->GetJointCount());
					outlet_anything(maxObexOutlet, gensym("jointcount"), outCount,outValue);

				} else if(EQUALS(message,"getcontactcount")) {
					outCount = 1;
					atom_setlong(outValue, world->GetContactCount());
					outlet_anything(maxObexOutlet, gensym("contactcount"), outCount,outValue);
				
				
				} else if(EQUALS(message,"applyforce") || EQUALS(message,"applyimpulse")) {
					if(ac == 5) {
					
						char* body_name = atom_getsym(av)->s_name;
						float forceX = atom_getfloat(av+1);
						float forceY = atom_getfloat(av+2);
						float pointX = P2M(atom_getfloat(av+3));
						float pointY = P2M(atom_getfloat(av+4));
						
						/* advance past name and param */
						ac--;
						av++;
						
						if(userDataManager->has(body_name)) {
										
							b2UserData* ud = userDataManager->get(body_name);
							
							if(ud->b2ObjectType == e_b2BodyType) {
								
														
																				
								if(EQUALS(message,"applyforce")) {
									((b2Body*)ud->b2Object)->ApplyForce(b2Vec2(forceX,forceY), b2Vec2(pointX,pointY));	

								} else {
									((b2Body*)ud->b2Object)->ApplyImpulse(b2Vec2(forceX,forceY), b2Vec2(pointX,pointY));	
								
								}
								
								return;
																																																														
																																																																										
							} else {
								error("%s is not a body.", body_name);
							}
						} else {
							error("no body called %s", body_name);
						}
						
						
						


					
					} else {
					
						if(EQUALS(message,"applyforce")) {
							error("applyforce BODY_NAME forceX forceY pointX pointY");
						} else {
							error("applyimpulse BODY_NAME impulseX impulseY pointX pointY");
						}
					
						return;
					
					}
				
				
				
				} else if(EQUALS(message,"applytorque")) {
				
					if(ac == 2) {
					
						char* body_name = atom_getsym(av)->s_name;
						float torque = atom_getfloat(av+1);


						/* advance past name and param */
						ac--;
						av++;
						
						if(userDataManager->has(body_name)) {
										
							b2UserData* ud = userDataManager->get(body_name);
							
							if(ud->b2ObjectType == e_b2BodyType) {
								((b2Body*)ud->b2Object)->ApplyTorque(torque);							
							} else {
								error("%s is not a body.", body_name);
							}
						} else {
							error("no body called %s", body_name);
						}
					
					} else {
						error("applytorque BODY_NAME toruqe");
						return;
					}
								
				} else if(EQUALS(message,"group")) {
					post("group -- ");
				
				} else {
					error("unknown input in outlet 0");
				}
	
				break;
			case 1:
				objectFactory->anything(s,ac,av);
				break;
			default:
				error("unknown inlet.");
		}

}


/* outlet	assist methods */
void b2d::assist(void *box, long msg, long arg, char *dst) {
	if (msg==ASSIST_INLET) { 
		switch (arg) { 
			case 0: 
				strcpy(dst,"Step, Draw and Real-time control."); 
				break; 
			case 1: 
				strcpy(dst,"Box2d Object Factory"); 
				break;
			} 
	} else if (msg==ASSIST_OUTLET) {
		switch (arg) { 
			case 0: 
				strcpy(dst,"Real-Time Ouput"); 
				break; 	
			case 1: 
				strcpy(dst,"Dumpout Attributes etc."); 
			break; 	
		} 
	}
}


void b2d::initMaxMSPAttributes(t_symbol *s, long ac, t_atom *av) {
	// create the main outlet 
	main_outlet = (t_object *)outlet_new(this, 0L);
	
	// create the two inlets
	m_proxy[0] = proxy_new(this,1,&m_inletNumber); 

	b2d * me_dummy;	// a dummy variable to this
	t_class * c  = (t_class*)maxClass;
	t_object * attr; 
	
	
	/* outputuserdata */
	attr = attr_offset_new("outputuserdata",_sym_long,0,(method)0L,(method)0L,calcoffset_cpp(me_dummy,outputuserdata));
	attr_addfilter_clip(attr,0,1,1,1); 
	class_addattr(c,attr);
	outputuserdata = 0;
	
	/* dosleep */ // clip it to 1/2
	attr = attr_offset_new("dosleep",_sym_long,0,(method)0L,(method)0L,calcoffset_cpp(me_dummy,dosleep));
	attr_addfilter_clip(attr,0,1,1,1); 
	class_addattr(c,attr);
	dosleep = 1;
	
	/* max bodies */
	class_addattr(c,attr_offset_new("maxbodies",_sym_long,0,(method)0L,(method)0L,calcoffset_cpp(me_dummy,maxbodies)));
	maxbodies = 1024;
	
	/* timestep */
	class_addattr(c,attr_offset_new("hz",_sym_float32,0,(method)0L,(method)0L,calcoffset_cpp(me_dummy,hz)));
	hz = 60.0f;
	
	/* iterations */
	class_addattr(c,attr_offset_new("iterations",_sym_long,0,(method)0L,(method)0L,calcoffset_cpp(me_dummy,iterations)));
	iterations = 10;
		
	/* world bounds */
	worldAABBcnt = 4;
	worldAABB[0] = -1024.0f;
	worldAABB[1] = -768.0f;
	worldAABB[2] = 2048.0f;
	worldAABB[3] = 1536.0f;
	
	class_addattr(c, attr_offset_array_new("worldAABB", // attr name 
									_sym_float32,// attr type (32-bit float) 
									4, // max size of array 
									0, // flags 
									(method)0L,
									METHOD_GIMMIE(b2d,setWorldAABB),	// set method
									calcoffset_cpp(me_dummy, worldAABBcnt), // count 
									calcoffset_cpp(me_dummy, worldAABB)	// attribute
									));
	
	/* debug draw flags */
	
	debugDrawFlagsCnt = 12;								
	
	debugDrawFlags[0] = 0;	// drawShapes
	debugDrawFlags[1] = 0;	// drawJoints
	debugDrawFlags[2] = 0;	// drawCoreShapes
	debugDrawFlags[3] = 0;	// drawAABBs
	debugDrawFlags[4] = 0;	// drawOBBs
	debugDrawFlags[5] = 0;	// drawPairs
	debugDrawFlags[6] = 0;	// drawContactPoints
	debugDrawFlags[7] = 0;	// drawContactNormals
	debugDrawFlags[8] = 0;	// drawContactForces
	debugDrawFlags[9] = 0;	// drawFrictionForces
	debugDrawFlags[10] = 0;	// drawCOMs (center of mass)
	debugDrawFlags[11] = 0;	// drawStats
	
	class_addattr(c, attr_offset_array_new("debugdrawflags", // attr name 
									_sym_long,// attr type (32-bit float) 
									debugDrawFlagsCnt, // max size of array 
									0, // flags 
									(method)0L,
									(method)0L,	// set method
									calcoffset_cpp(me_dummy, debugDrawFlagsCnt), // count 
									calcoffset_cpp(me_dummy, debugDrawFlags)	// attribute
									));

	
	/* solver params */
	solverParamsCnt = 3;
	
	solverParams[0] = 1;	// enableWarmStarting
	solverParams[1] = 1;	// enable positioncorrection
	solverParams[2] = 1;	// enable TOI (time of impact)

	class_addattr(c, attr_offset_array_new("solverparams", // attr name 
									_sym_long,// attr type (32-bit float) 
									solverParamsCnt, // max size of array 
									0, // flags 
									(method)0L,
									(method)0L,	// set method
									calcoffset_cpp(me_dummy, solverParamsCnt), // count 
									calcoffset_cpp(me_dummy, solverParams)	// attribute
									));


	/* gravity */
	
	gravitycnt = 2;
	gravity[0] = 0.0f;
	gravity[1] = 10.0f;
	
	class_addattr(c, attr_offset_array_new("gravity", // attr name 
									_sym_float32,// attr type (32-bit float) 
									2, // max size of array 
									0, // flags 
									(method)0L, //default getter
									METHOD_GIMMIE(b2d,setGravity),	// custom set method
									calcoffset_cpp(me_dummy, gravitycnt), // count 
									calcoffset_cpp(me_dummy, gravity)	// attribute
									));
	
	
	// remember to set any defaults before attribute args are processed!
	attr_args_process(this, ac, av);
	
}

void b2d::ShapeDestroyed(b2Shape* shape) {

	b2UserData* ud = (b2UserData*)shape->GetUserData();

	if(ud != NULL) {
		userDataManager->remove(ud->name);
	} else {
		error("shape called had NULL user data!!");
	}


	
}

void b2d::JointDestroyed(b2Joint* joint) {
	b2UserData* ud = (b2UserData*)joint->GetUserData();

	if(ud != NULL) {
		userDataManager->remove(ud->name);
	} else {
		error("joint called had NULL user data!!");
	}
}
void DestructionListener::SayGoodbye(b2Joint* joint)

{
	post("JOINT DESTROYED!!");


	if (b2d_main->m_mouseJoint == joint)
	{
		b2d_main->m_mouseJoint = NULL;
	}
	else
	{
		b2d_main->JointDestroyed(joint);
	}
	
	
}

void DestructionListener::SayGoodbye(b2Shape* shape)
{
	
	post("SHAPE DESTROYED!!");
	
	b2d_main->ShapeDestroyed(shape);
	
}







// called when library is loaded
extern "C" int main(void)
{	
	t_class * c = b2d::maxMakeClass("box2d"); // in MAX5 this class name is what must be called
		
	// add some methods:
	REGISTER_METHOD(b2d, bang);
	REGISTER_METHOD(b2d, step);			// the step method
	REGISTER_METHOD_GIMME(b2d, mouse);
	
	
	/* these are the getters and setters for bodies, shapes and joints */
	REGISTER_METHOD_GIMME(b2d,get);
	REGISTER_METHOD_GIMME(b2d,set);

	REGISTER_METHOD_GIMME(b2d,grab);


	REGISTER_METHOD_GIMME(b2d,group);


	REGISTER_METHOD(b2d, reset);
	

	// the catch all method
	REGISTER_METHOD_GIMME(b2d,anything);

	// the assist method
	REGISTER_METHOD_ASSIST(b2d,assist);

	
	/* this must be done as a last step */
	// register this as a class for Max UI & store static reference
	class_register(CLASS_BOX, c);	

	/* IMPORTANT: The class_register() function should be called at the end of 
		main(), only after all methods and attributes have been defined.*/
	

	printf("CREATING B@D IN THE CONSOLUE\n\n\n\n");
		//printf("area = %f, B2_FLT_EPSILON = %f",1.0f,1.0f);


}



